package service

import (
	"fmt"
	"time"

	"gitee.com/huangxinbo/xcas/internal/app/base"
	"gitee.com/huangxinbo/xcas/internal/constants"
	"gitee.com/huangxinbo/xcas/internal/dao"
	"gitee.com/huangxinbo/xcas/internal/model/db_model"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

type CasService struct {
	base    *base.Base
	dao     *dao.DAO
	service *Service
}

func NewCasService(base *base.Base, dao *dao.DAO, service *Service) *CasService {
	return &CasService{
		base:    base,
		dao:     dao,
		service: service,
	}
}

// generateTicketID 生成票证ID - 使用UUID v7（基于时间戳，可排序）
func (s *CasService) generateTicketID() (string, error) {
	uuid, err := uuid.NewV7()
	if err != nil {
		return "", err
	}
	return "TGT-" + uuid.String(), nil
}

// generateServiceTicket 生成服务票证 - 使用UUID v4（完全随机）
func (s *CasService) generateServiceTicket() (string, error) {
	uuid, err := uuid.NewRandom()
	if err != nil {
		return "", err
	}
	return "ST-" + uuid.String(), nil
}

// CreateTGT 创建TGT (Ticket Granting Ticket)
func (s *CasService) CreateTGT(ctx *gin.Context, principalID uuid.UUID, rememberMe bool) (*db_model.CasTicket, error) {
	tgtID, err := s.generateTicketID()
	if err != nil {
		s.base.Logger.Error("Failed to generate TGT ID", zap.Error(err))
		return nil, err
	}

	// 设置TGT过期时间
	var expireTime time.Time
	if rememberMe {
		// 记住我：30天
		expireTime = time.Now().Add(constants.TGTRememberMaxLifetime)
	} else {
		// 不记住我：8小时
		expireTime = time.Now().Add(constants.TGTMaxLifetime)
	}

	tgt := &db_model.CasTicket{
		ID:          tgtID,
		PrincipalID: principalID,
		Type:        db_model.TicketTypeTGT,
		ExpiredAt:   expireTime,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
		Remember:    rememberMe,
	}

	// 使用 Redis DAO 创建 TGT
	err = s.dao.CasTicketRedisDAO.CreateTGT(ctx, tgt)
	if err != nil {
		s.base.Logger.Error("Failed to create TGT", zap.Error(err))
		return nil, err
	}

	s.base.Logger.Info("TGT created successfully",
		zap.String("tgt_id", tgtID),
		zap.String("principal_id", principalID.String()),
		zap.Time("expire_time", expireTime))

	return tgt, nil
}

// CreateST 创建ST (Service Ticket)
func (s *CasService) CreateST(ctx *gin.Context, tgtID string, serviceURL string) (*db_model.CasTicket, error) {
	// 验证TGT是否存在且未过期
	tgt, err := s.dao.CasTicketRedisDAO.GetTGTByID(ctx, tgtID)
	if err != nil {
		s.base.Logger.Error("Failed to get TGT", zap.Error(err))
		return nil, err
	}

	if tgt == nil {
		return nil, fmt.Errorf("TGT not found")
	}

	if time.Now().After(tgt.ExpiredAt) {
		return nil, fmt.Errorf("TGT expired")
	}

	// 生成ST
	stID, err := s.generateServiceTicket()
	if err != nil {
		s.base.Logger.Error("Failed to generate ST ID", zap.Error(err))
		return nil, err
	}

	// ST过期时间：5分钟
	expireTime := time.Now().Add(5 * time.Minute)

	st := &db_model.CasTicket{
		ID:          stID,
		PrincipalID: tgt.PrincipalID,
		ParentID:    tgtID,
		Type:        db_model.TicketTypeST,
		ExpiredAt:   expireTime,
		CreatedAt:   time.Now(),
	}

	// 使用 Redis DAO 创建 ST
	err = s.dao.CasTicketRedisDAO.CreateST(ctx, st)
	if err != nil {
		s.base.Logger.Error("Failed to create ST", zap.Error(err))
		return nil, err
	}

	s.base.Logger.Info("ST created successfully",
		zap.String("st_id", stID),
		zap.String("tgt_id", tgtID),
		zap.String("service", serviceURL),
		zap.Time("expire_time", expireTime))

	return st, nil
}

// ValidateST 验证服务票证
func (s *CasService) ValidateST(ctx *gin.Context, stID string, serviceURL string) (*db_model.CasTicket, error) {
	st, err := s.dao.CasTicketRedisDAO.GetSTByID(ctx, stID)
	if err != nil {
		s.base.Logger.Error("Failed to get ST", zap.Error(err))
		return nil, err
	}

	if st == nil {
		return nil, fmt.Errorf("ST not found")
	}

	if st.Type != db_model.TicketTypeST {
		return nil, fmt.Errorf("ST invalid")
	}

	if time.Now().After(st.ExpiredAt) {
		return nil, fmt.Errorf("ST expired")
	}

	// 验证通过后删除ST（一次性使用）
	err = s.dao.CasTicketRedisDAO.DeleteST(ctx, stID)
	if err != nil {
		s.base.Logger.Error("Failed to delete ST after validation", zap.Error(err))
		// 即使删除失败，也返回成功验证，但记录错误
	}

	s.base.Logger.Info("ST validated successfully",
		zap.String("st_id", stID),
		zap.String("service", serviceURL))

	return st, nil
}

// GetTGTByID 根据ID获取TGT
func (s *CasService) GetTGTByID(ctx *gin.Context, tgtID string) (*db_model.CasTicket, error) {
	tgt, err := s.dao.CasTicketRedisDAO.GetTGTByID(ctx, tgtID)
	if err != nil {
		return nil, err
	}

	if tgt == nil || tgt.Type != db_model.TicketTypeTGT {
		return nil, fmt.Errorf("TGT not found")
	}

	return tgt, nil
}

// UpdateTGTExpiration 更新TGT相关Redis键的过期时间
func (s *CasService) UpdateTGTExpiration(ctx *gin.Context, tgtID string, expiredAt time.Time) error {
	// 获取TGT信息
	tgt, err := s.dao.CasTicketRedisDAO.GetTGTByID(ctx, tgtID)
	if err != nil {
		return err
	}

	if tgt == nil {
		return fmt.Errorf("TGT not found")
	}

	// 计算剩余时间
	remainingTime := time.Until(expiredAt)
	if remainingTime <= 0 {
		return fmt.Errorf("TGT already expired")
	}

	// 更新Redis中TGT相关键的过期时间
	err = s.dao.CasTicketRedisDAO.UpdateTGTExpiration(ctx, tgtID)
	if err != nil {
		s.base.Logger.Error("Failed to update TGT expiration in Redis", zap.Error(err))
		return err
	}

	return nil
}

// DeleteTGT 删除TGT
func (s *CasService) DeleteTGT(ctx *gin.Context, tgtID string) error {
	err := s.dao.CasTicketRedisDAO.DeleteTGT(ctx, tgtID)
	if err != nil {
		s.base.Logger.Error("Failed to delete TGT", zap.Error(err))
		return err
	}

	s.base.Logger.Info("TGT deleted successfully", zap.String("tgt_id", tgtID))
	return nil
}

// CleanExpiredTickets 清理过期票证
func (s *CasService) CleanExpiredTickets(ctx *gin.Context) error {
	return s.dao.CasTicketRedisDAO.CleanExpiredTickets(ctx)
}
